package com.gsk.europepharma.salesReport.utils;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import net.sf.json.JSONArray;

public class CreateCarto {

	private final static int WIDTH_A0 = 3088;
	private final static int HEIGHT_A0 = 2969;
	
	private final static int WIDTH = 500;
	private final static int HEIGHT = 500;
	
	public static BufferedImage createCarto(JSONArray jsonArrayPixels, int xmin, int ymin, int xmax, int ymax, JSONArray jsonArrayPixelsPm, HttpServletRequest request, String typeCarto) throws IOException {

		BufferedImage bufferedImage = new BufferedImage(WIDTH_A0, HEIGHT_A0, BufferedImage.TYPE_INT_RGB);

		Graphics2D g2d = bufferedImage.createGraphics();

		g2d.setColor(Color.white);
		g2d.fillRect(0, 0, WIDTH_A0, HEIGHT_A0);

		g2d.setColor(Color.black);
		
		for (int i = 0; i < jsonArrayPixels.size(); i++) {
			if (((Integer) jsonArrayPixels.getJSONObject(i).get("color")) == 1) {
				g2d.setColor(Color.BLACK);
			} else if (((Integer) jsonArrayPixels.getJSONObject(i).get("color")) == 2) {
				g2d.setColor(Color.LIGHT_GRAY);
			} 
			g2d.fillRect((int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("x").toString()))), 
						(int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("y").toString()))), 
						1, 
						1);
		}
		
		HttpSession sessionObject = request.getSession();
		
		// Les legendes 
		Legende legendeRegion = (Legende) sessionObject.getAttribute("legendeRegion");
		Legende legendeSecteur = (Legende) sessionObject.getAttribute("legendeSecteur");
		
		// Indice de recherche pour la legende
		int indice = 0;
		
		// Valeurs utiles pour les legendes de type 'Nombre egaux' 
		int nbValuesByTrancheRegion = 0;
		if (legendeRegion != null && legendeRegion.getCount() != null && legendeRegion.getNbTranches() != null && legendeRegion.getNbTranches() != 0) {
			nbValuesByTrancheRegion = (int) Math.round(Double.parseDouble(legendeRegion.getCount() + "") / Double.parseDouble(legendeRegion.getNbTranches() + ""));
		}
		if (nbValuesByTrancheRegion == 0) {
			nbValuesByTrancheRegion = 1;
		}
		int nbValuesByTrancheSecteur = 0;
		if (legendeSecteur != null && legendeSecteur.getCount() != null && legendeSecteur.getNbTranches() != null && legendeSecteur.getNbTranches() != 0) {
			nbValuesByTrancheSecteur = (int) Math.round(Double.parseDouble(legendeSecteur.getCount() + "") / Double.parseDouble(legendeSecteur.getNbTranches() + ""));
		}
		if (nbValuesByTrancheSecteur == 0) {
			nbValuesByTrancheSecteur = 1;
		}
		int nbValuesTemp = 0;
		String tmpRegionSecteurCurrent = "";
		
		for (int i = 0; i < jsonArrayPixelsPm.size(); i++) {
			Point p = new Point((int) ((Integer.parseInt(jsonArrayPixelsPm.getJSONObject(i).get("x").toString()))), 
								(int) ((Integer.parseInt(jsonArrayPixelsPm.getJSONObject(i).get("y").toString()))));
			
			Double val = Double.parseDouble(jsonArrayPixelsPm.getJSONObject(i).get("val").toString());
			
			String tmpNiveau = jsonArrayPixelsPm.getJSONObject(i).get("niveau").toString();
			if (i == 0) {
				tmpRegionSecteurCurrent = tmpNiveau;
			}
			
			// Gestion des legendes pour les cartes 
			
			if (typeCarto.equals("Region")) { // Carte de gauche (region)
				
				if (legendeRegion.getTypeClassement().equals("PE") || legendeRegion.getTypeClassement().equals("DU")) {
					
					// Type de classement = { Plages egales ou definition utilisateur }
					Color color = findColorInPalettes(legendeRegion.getPalettes(), val, indice);
					floodFill(bufferedImage, p, Color.white, color);
					
				} else {
					
					// Type de classement = { Nombre egaux }
					if (!tmpRegionSecteurCurrent.equals(tmpNiveau)) {
						nbValuesTemp++;
						tmpRegionSecteurCurrent = tmpNiveau;
						if (nbValuesTemp == nbValuesByTrancheRegion && ((indice + 1) < legendeRegion.getPalettes().size())) {
							nbValuesTemp = 0;
							indice++;
						}
					}
					int intValue = Integer.parseInt(legendeRegion.getPalettes().get(indice).getColor().substring(1, 7), 16); 
					Color color = new Color(intValue); 
					floodFill(bufferedImage, p, Color.white, color);
					
				}
				
			} else { // Carte de droite (secteur)
				
				if (legendeSecteur.getTypeClassement().equals("PE") || legendeSecteur.getTypeClassement().equals("DU")) {
					
					// Type de classement = { Plages egales ou definition utilisateur }
					Color color = findColorInPalettes(legendeSecteur.getPalettes(), val, indice);
					floodFill(bufferedImage, p, Color.white, color);
					
				} else {
					
					// Type de classement = { Nombre egaux }
					if (!tmpRegionSecteurCurrent.equals(tmpNiveau)) {
						nbValuesTemp++;
						tmpRegionSecteurCurrent = tmpNiveau;
						if (nbValuesTemp == nbValuesByTrancheSecteur && ((indice + 1) < legendeSecteur.getPalettes().size())) {
							nbValuesTemp = 0;
							indice++;
						}
					}
					int intValue = Integer.parseInt(legendeSecteur.getPalettes().get(indice).getColor().substring(1, 7), 16); 
					Color color = new Color(intValue); 
					floodFill(bufferedImage, p, Color.white, color);
					
				}

			}
			
		}

		if (request != null) { // Dessin de la carte 1
			for (int i = 0; i < jsonArrayPixels.size(); i++) {
				if (((Integer) jsonArrayPixels.getJSONObject(i).get("color")) == 1) {
					g2d.setColor(Color.BLACK);
					g2d.fillRect((int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("x").toString()))) + 1, 
							(int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("y").toString()))), 
							1, 
							1);
					g2d.fillRect((int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("x").toString()))) + 1, 
							(int) ((Integer.parseInt(jsonArrayPixels.getJSONObject(i).get("y").toString()))) + 1, 
							1, 
							1);
				} else if (((Integer) jsonArrayPixels.getJSONObject(i).get("color")) == 2) {
					g2d.setColor(Color.LIGHT_GRAY);
				} 
			}
		}
		
		g2d.dispose();
		
		BufferedImage resizedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = resizedImage.createGraphics();
		g.setColor(Color.white);
		g.fillRect(0, 0, WIDTH, HEIGHT);
		g.setColor(Color.black);
		int max = Math.max(xmax - xmin, ymax - ymin) + 1;
		
		xmin = xmin - (max - (xmax - xmin)) / 2;
		ymin = ymin - (max - (ymax - ymin)) / 2;
		
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

		g.drawImage(bufferedImage, 0, 0, WIDTH, HEIGHT, xmin, ymin, xmin + max, ymin + max, null);
		g.dispose();
		
		// Mise en session des variables pour la mise a l echelle
		if (typeCarto.equals("Region")) { // Dessin de la carte 1
	        sessionObject.setAttribute("xMinCarte1", xmin);
	        sessionObject.setAttribute("yMinCarte1", ymin);
	        sessionObject.setAttribute("maxCarte1", max);
		} else {
	        sessionObject.setAttribute("xMinCarte2", xmin);
	        sessionObject.setAttribute("yMinCarte2", ymin);
	        sessionObject.setAttribute("maxCarte2", max);
		}
		
		return resizedImage;
	}
	
	/**
	 * Recherche une valeur 'val' dans un ensemble de plages de valeurs 'palettes'.
	 * 
	 * @param palettes
	 * 		liste des plages de valeurs
	 * @param val
	 * 		la valeur
	 * @param indice
	 * 		l'indice à partir duquel on recherche dans la liste des palettes
	 * @return
	 * 		la couleur correspondant à la palette
	 */
	private static Color findColorInPalettes(List<Palette> palettes, Double val, int indice) {
		int intFirstValue = Integer.parseInt(palettes.get(indice).getColor().substring(1, 7), 16); 
		Color color = new Color(intFirstValue); 
		for (int i = indice; i < palettes.size(); i++) {
			if (val >= palettes.get(i).getMin() && val <= palettes.get(i).getMax()) {
				int intValue = Integer.parseInt(palettes.get(i).getColor().substring(1, 7), 16); 
				color = new Color(intValue); 
				indice = i;
				return color;
			}
		}
		return color;
	}
	
	/**
	 * Algorithme de remplissage d'une forme.
	 */
	public static void floodFill(BufferedImage image, Point node, Color targetColor, Color replacementColor) {
		int width = image.getWidth();
		int height = image.getHeight();
		int target = targetColor.getRGB();
		int replacement = replacementColor.getRGB();
		if (target != replacement) {
			Deque<Point> queue = new LinkedList<Point>();
			do {
				int x = node.getX();
				int y = node.getY();
				while (x > 0 && image.getRGB(x - 1, y) == target) {
					x--;
				}
				boolean spanUp = false;
				boolean spanDown = false;
				while (x < width && image.getRGB(x, y) == target) {
					image.setRGB(x, y, replacement);
					if (!spanUp && y > 0 && image.getRGB(x, y - 1) == target) {
						queue.add(new Point(x, y - 1));
						spanUp = true;
					} else if (spanUp && y > 0 && image.getRGB(x, y - 1) != target) {
						spanUp = false;
					}
					if (!spanDown && y < height - 1 && image.getRGB(x, y + 1) == target) {
						queue.add(new Point(x, y + 1));
						spanDown = true;
					} else if (spanDown && y < height - 1 && image.getRGB(x, y + 1) != target) {
						spanDown = false;
					}
					x++;
				}
			} while ((node = queue.pollFirst()) != null);
		}
	}
	
}
